﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Text.RegularExpressions;


namespace SubsDownloader.Core.Providers.OpenSubtitles
{
    public static class OpenSubtitlesUtils
    {
        #region MovieHash

        public static byte[] ComputeMovieHash( string filename )
        {
            byte[] result;
            using ( Stream input = File.OpenRead( filename ) )
            {
                result = ComputeMovieHash( input );
            }
            return result;
        }

        public static byte[] ComputeMovieHash( Stream input )
        {
            long streamsize = input.Length;
            long lhash = streamsize;

            long i = 0;
            var buffer = new byte[sizeof ( long )];
            while ( i < 65536 / sizeof ( long ) && (input.Read( buffer, 0, sizeof ( long ) ) > 0) )
            {
                i++;
                lhash += BitConverter.ToInt64( buffer, 0 );
            }

            input.Position = Math.Max( 0, streamsize - 65536 );
            i = 0;
            while ( i < 65536 / sizeof ( long ) && (input.Read( buffer, 0, sizeof ( long ) ) > 0) )
            {
                i++;
                lhash += BitConverter.ToInt64( buffer, 0 );
            }
            input.Close();
            byte[] result = BitConverter.GetBytes( lhash );
            Array.Reverse( result );
            return result;
        }

        public static string ToHexadecimal( byte[] bytes )
        {
            var hexBuilder = new StringBuilder();
            for ( int i = 0 ; i < bytes.Length ; i++ )
            {
                hexBuilder.Append( bytes[i].ToString( "x2" ) );
            }
            return hexBuilder.ToString();
        }

        #endregion

        #region RecurseSubDirs

        public static string[] GetFilesByExtensions( string path, string extensions, SearchOption option )
        {
            var result = new List<string>();
            InternalGetFiles( path, option, extensions.Split( ';' ), result );
            return result.ToArray();
        }

        private static void InternalGetFiles( string path, SearchOption option, IEnumerable<string> extensions,
                                              List<string> result )
        {
            // Get the files.
            string[] files = Directory.GetFiles( path );
            foreach ( var file in files )
            {
                // Get the extension of the file.
                string ext = Path.GetExtension( file );
                // Validate the extension against the searched extensions.
                foreach ( var p in extensions )
                {
                    if ( ext == p )
                    {
                        result.Add( file );
                        break;
                    }
                }
            }

            // Returns if only top directory is requested.
            if ( option == SearchOption.TopDirectoryOnly )
                return;
            // Get all other directories.
            foreach ( var directory in Directory.GetDirectories( path ) )
            {
                InternalGetFiles( directory, option, extensions, result );
            }
        }

        #endregion

        #region DecodeAndDecompress

        public static byte[] Decode( string str )
        {
            byte[] decbuff = Convert.FromBase64String( str );
            return decbuff;
        }

        public static byte[] DecodeAndDecompress( string str )
        {
            return Decompress( Decode( str ) );
        }

        public static byte[] Decompress( byte[] b )
        {
            var ms = new MemoryStream( b.Length );
            ms.Write( b, 0, b.Length );
            // last 4 bytes of GZipStream = length of decompressed data
            ms.Seek( -4, SeekOrigin.Current );
            var lb = new byte[4];
            ms.Read( lb, 0, 4 );
            int len = BitConverter.ToInt32( lb, 0 );
            ms.Seek( 0, SeekOrigin.Begin );
            var ob = new byte[len];
            var zs = new GZipStream( ms, CompressionMode.Decompress );
            zs.Read( ob, 0, len );
            return ob;
        }

        #endregion

        #region LangCodes

        public static List<LangMap> GenerateLangMap()
        {
            var theLangMap = new List<LangMap>();
            theLangMap.Add( new LangMap( "cs", "cze,ces", "Czech" ) );
            theLangMap.Add( new LangMap( "fi", "fin", "Finnish" ) );
            theLangMap.Add( new LangMap( "tr", "tur", "Turkish" ) );
            theLangMap.Add( new LangMap( "sv", "swe", "Swedish" ) );
            theLangMap.Add( new LangMap( "sk", "slo,slk", "Slovak" ) );
            theLangMap.Add( new LangMap( "bg", "bul", "Bulgarian" ) );
            theLangMap.Add( new LangMap( "fr", "fre,fra", "French" ) );
            theLangMap.Add( new LangMap( "ar", "ara", "Arabic" ) );
            theLangMap.Add( new LangMap( "vi", "vie", "Vietnamese" ) );
            theLangMap.Add( new LangMap( "sr", "scc,srp", "Serbian" ) );
            theLangMap.Add( new LangMap( "mk", "mac,mkd", "FYROM" ) );
            theLangMap.Add( new LangMap( "ko", "kor", "Korean" ) );
            theLangMap.Add( new LangMap( "et", "est", "Estonian" ) );
            theLangMap.Add( new LangMap( "zh", "chi,zho", "Chinese" ) );
            theLangMap.Add( new LangMap( "hr", "scr,hrv", "Croatian" ) );
            theLangMap.Add( new LangMap( "gl", "glg", "Galician" ) );
            theLangMap.Add( new LangMap( "ja", "jpn", "Japanese" ) );
            theLangMap.Add( new LangMap( "el,gr", "gre,ell", "Greek" ) );
            theLangMap.Add( new LangMap( "en", "eng", "English" ) );
            theLangMap.Add( new LangMap( "da", "dan", "Danish" ) );
            theLangMap.Add( new LangMap( "de", "ger,deu", "German" ) );
            theLangMap.Add( new LangMap( "he", "heb", "Hebrew" ) );
            theLangMap.Add( new LangMap( "id", "ind", "Indonesian" ) );
            theLangMap.Add( new LangMap( "it", "ita", "Italian" ) );
            theLangMap.Add( new LangMap( "es", "spa", "Spanish" ) );
            theLangMap.Add( new LangMap( "no", "nor", "Norwegian" ) );
            theLangMap.Add( new LangMap( "nl", "dut,nld", "Dutch" ) );
            theLangMap.Add( new LangMap( "pl", "pol", "Polish" ) );
            theLangMap.Add( new LangMap( "fa", "per,fas", "Persian" ) );
            theLangMap.Add( new LangMap( "hu", "hun", "Hungarian" ) );
            theLangMap.Add( new LangMap( "pt", "por", "Portuguese" ) );
            theLangMap.Add( new LangMap( "ro", "rum,ron", "Romanian" ) );
            theLangMap.Add( new LangMap( "ru", "rus", "Russian" ) );
            return theLangMap;
        }

        public static string ParseLangCodeFromFile( string filename, List<LangMap> langs )
        {
            string name = Path.GetFileNameWithoutExtension( filename );
            string[] nameSplit = name.Split( '.' );
            string langCode = nameSplit[nameSplit.Length - 1];
            foreach ( var l in langs )
            {
                if ( l.two.Contains( langCode ) )
                {
                    return langCode;
                }
            }
            return "";
        }

        #endregion

        public static string FilenameFromTitle( string name )
        {
            // first trim the raw string
            string safe = name.Trim();

            // trim out illegal characters
            safe = Regex.Replace( safe, "[^a-zA-Z0-9\\- ]", "" );

            return safe;
        }
    }
}